#include "Samolot.h"
#include "SamolotMessage.h"
#include "pair.h"
#include <cmath>
#include <unistd.h>

#define TIMESCALE 30
#define COILEITERACJISPRAWDZAC 5
#define SKALAAWARII 10
#include "../m_connection_tests/komunikacja1.2.h"


void Samolot::setZamiastKonstruktora( int _idLotniskaSrc, int _idLotniskaDst, std::string typ)
{

	liczbaLotnisk=6666;
	lvlPaliwa=-666;
	zuzyciePaliwa=-666;
	v=-666;
	gotowyDoStartu=false;
	naZiemiJest=true;
	
	idLotniskaDst = _idLotniskaDst;
	idLotniskaSrc = _idLotniskaSrc;
	idAktWiezy  = _idLotniskaSrc;

	COUT_SAM("Odpalam konstruktor\n");
	timeScale=60;	
	typSamolotu=typ;
	SamolotMessage msg;
	
	msg.setMsgDst(BD_MOD);
	msg.setSendMsgType(MSG_PODAJ_LICZBE_LOTNISK);
	COUT_SAM("Prosze o liczbe lotnisk\n");
	msg.sendSamolotMessage();
	msg.setMsgDst(BD_MOD);
	msg.setSendMsgType(MSG_PRZESLIJ_LOTNISKA);
	COUT_SAM("Prosze o liste lotnisk\n");
	msg.sendSamolotMessage();
	
	
	sleep(1);
	
	msg.setMsgDst(BD_MOD);
	msg.setSendMsgType(MSG_ZAPODAJ_DANE_TECHNICZNE);
	msg.setTypSamolotu(typ);
	COUT_SAM("Prosze o dane samolotu\n");
	msg.sendSamolotMessage();
	
	//uruchamiam watki...
	init();
	

}


void SamolotMessage::sendSamolotMessage(void)
{

	char * nazwa;
		
	Message * mes = new Message();

	mes->setType( sendMsgType);
	switch( sendMsgType) {

		case MSG_PODAJ_LICZBE_LOTNISK :
          	mes->setConnReceiverName("DATABASE");
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	sendMessage( *mes);
          	break;
          	
      case MSG_PRZESLIJ_LOTNISKA :
          	mes->setConnReceiverName("DATABASE");
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	sendMessage( *mes); 	
			break;
			
	 case MSG_ZAPODAJ_DANE_TECHNICZNE :
          	mes->setConnReceiverName("DATABASE");
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	mes->setStringArg1( (char*)typSamolotu.c_str());
          	sendMessage( *mes);
			break;
			
	  case MSG_AKTUALNA_POZYCJA :
          	mes->setConnReceiverName(Connector::getInstance()->getAirportName(idHelper));
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	mes->setFloatArg1( pos.first);
          	mes->setFloatArg2( pos.second);
          	sendMessage( *mes);
			break;
			
	   case MSG_NOWA_MASZYNA_W_STREFIE :
          	mes->setConnReceiverName( Connector::getInstance()->getAirportName(idWiezy));
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	mes->setStringArg1( (char*)typSamolotu.c_str());
          	sendMessage( *mes);
			break;
			
	 case MSG_OPUSCILES_STREFE_WPLYWOW :
          	mes->setConnReceiverName(Connector::getInstance()->getAirportName(idWiezy));
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	sendMessage( *mes);
			break;
			
	  case MSG_ZGLOSZENIE_GOTOWOSCI_DO_STARTU :
          	mes->setConnReceiverName( Connector::getInstance()->getAirportName(idHelper) );
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	mes->setStringArg1( (char*)typSamolotu.c_str());
          	sendMessage( *mes);
			break;
			
	case MSG_KONCZE_SMIGAC	 :
          	mes->setConnReceiverName("EVENTS");
          	cout << "nazwa lotniska ; " <<getNazwaLotniska()<<endl;
          	mes->setStringArg1( (char*)getNazwaLotniska().c_str());
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	sendMessage( *mes);
			break;
			
	case MSG_ZGLOSZENIE_GOTOWOSCI_DO_LADOWANIA	 :
          	mes->setConnReceiverName(Connector::getInstance()->getAirportName(idHelper));
          	mes->setConnSenderName(Connector::getInstance()->getConnName());
          	mes->setStringArg1( (char*)typSamolotu.c_str());
          	sendMessage( *mes);
			break;		
			
	 case MSG_ZAPODAJ_WSPOLRZEDNE_LOTNISKA	 :
          	mes->setConnReceiverName("DATABASE");
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	mes->setIntArg1(idWiezy);
          	sendMessage( *mes);
			break;

	 case  MSG_POJAWIAM_SIE_NA_LOTNISKU	 :
          	mes->setConnReceiverName(Connector::getInstance()->getAirportName(idHelper));
          	mes->setConnSenderName( Connector::getInstance()->getConnName());
          	mes->setStringArg1( (char*)typSamolotu.c_str());
          	sendMessage( *mes);
			break;
			
		default:
			COUT_SAM ("ERROR : nieznany typ message. Powiadomic Lukasza, albo sie ogarnac !\n");
			break;;
		}
	

}

void* Samolot::startRecounting(void*a)
{
	prevTimeMeas= time(NULL);
	while(isRunning && !naZiemiJest)
	{
		recountPos();
		float odleglosc = obliczOdleglosc(currPos,dstPos);

		if(odleglosc<fajnaOdlegloscOdLotniska)
		{
			COUT_SAM("prosze o pozwolenie\n");
			poprosOPozwolenieNaLadowanie();
		}
		COUT_SAM("( " <<currPos.first<<" , " <<currPos.second<<" )" <<std::endl);
		COUT_SAM("zasypiam na "<<czasOczekiwania<<"\n");
		sleep(czasOczekiwania);
		czasOczekiwania=2;
	}
}

void Samolot::recountPos()
{
	long currTime = time(NULL);
	long deltaTime = (currTime-prevTimeMeas)*timeScale;
 	currPos=calcNewPos(currPos,dstPos,v,deltaTime);
 	
 	lvlPaliwa-= (v*deltaTime)*zuzyciePaliwa;
 	COUT_SAM( "poziom paliwa: "<<lvlPaliwa <<"\n");
 	if(lvlPaliwa<=0)
 	{
 		COUT_SAM("konczy sie paliwo\n");
 		rozpatrzAwarie(AWARIA_SILNIKA,5,"----");
	}
	prevTimeMeas = currTime;
}

position Samolot:: calcNewPos (position a, position b, float v, int deltaTime)
{
	position  newPos=a;
	position begPosA = a;
	position begPosB = b;
	
	float distance = v*deltaTime;

	a.first = (float)(pi*a.first)/180;
	a.second = (float)(pi*a.second)/180;
	b.first = (float)(pi*b.first)/180;
	b.second = (float)(pi*b.second)/180;

	float dist_Y = 6378 * (b.first - a.first);
	float dist_X = 6378 * (b.second - a.second);

	// bo tg(alfa) = distY/distX, wiec
	float alfa = atan(dist_Y/dist_X);

	newPos.first = abs(sin(alfa)*distance);
	newPos.second = abs(cos(alfa)*distance);


	//newPos.lat /= 6378000;
	//newPos.lon /= 6378000;

	newPos.first *= (float)(180/(pi*6378)) ;
	newPos.second *= (float)(180/(pi*6378)) ;	
	
	newPos.first =(begPosA.first>begPosB.first ? (-1)  : (1) )*newPos.first+ begPosA.first;
	newPos.second= (begPosA.second>begPosB.second ? (-1)  : (1) )*newPos.second+ begPosA.second;
	
return newPos;
}
 

int Samolot::getTimeInSec()
{
	return clock()/CLOCKS_PER_SEC;
}
void* Samolot::startSending(void*a)
{
	int counter=0;
	while(isRunning&& !naZiemiJest)
	{
		++counter;
//		cout<<"Counter od sprawdzania lotniska: "<<counter<<" / "<<COILEITERACJISPRAWDZAC<<"\n";
		if(counter==COILEITERACJISPRAWDZAC)
		{
			WiezaInfo info = znajdzNajblizszeLotnisko();
			COUT_SAM("Sprawdzam najblizsze lotnisko - efekt: "<<info.id);
			if(info.id!=idAktWiezy)
			{
   				powiadomOOpuszczeniuStrefy();
				idAktWiezy=info.id;								
				powiadomOZmianieWiezy();				
			}
			counter =0;
		}
		SamolotMessage msg;
		msg.setMsgDst(WIEZA_MOD);
		msg.setPosition(currPos);
		msg.setIdHelper(idAktWiezy);
		msg.setSendMsgType(MSG_AKTUALNA_POZYCJA);
		msg.sendSamolotMessage();
		sleep(1);
	}
}


static void* startSendingG(void*a)
{
		COUT_SAM("zaczynam wysylanie pozycji\n");
    Samolot ins=Samolot::getInstance();
	static_cast<Samolot>(ins).startSending(a);
	
}

static void* startRecountingG(void*a)
{
	COUT_SAM("zaczynam obliczanie pozycji\n");
    Samolot ins=Samolot::getInstance();
	static_cast<Samolot>(ins).startRecounting(a);
}

void Samolot::init()
{ 			
		pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );

		/*
		srcPos.first=40;	
		srcPos.second=30;
		
		dstPos.first=80;
		dstPos.second=50;
		
		currPos = srcPos;
		*/
		isRunning= true;
		while(idLotniskaSrc==-1 || idLotniskaDst==-1 || (liczbaLotnisk != lotniska.size())||lvlPaliwa==-666||v==-666||zuzyciePaliwa==-666)
		{
			COUT_SAM("czekam na  zmienne inicjacyjne\n");
			sleep(2);
		}
		currPos = srcPos;
		powiadomLotniskoOPojawieniuSie();
		sleep(1);
		while(!gotowyDoStartu)
		{
			COUT_SAM("czekam na pozwolenie na start\n");
			wyslijGotowoscDoStartu();			
			sleep(czasOczekiwania);
			czasOczekiwania=2;

		}
		naZiemiJest=false;
		COUT_SAM("wystartowalem!!\n");
		pthread_create( NULL, &attr, &startSendingG, NULL );
		pthread_create( NULL, &attr, &startRecountingG, NULL );
		while(isRunning)
		{
			sleep(2);
		}					
}
void Samolot:: pickMessage(SamolotMessage msg)
{
	const int msgType = msg.getReceiveMsgType();
	COUT_SAM("dostalem wiadomosc\n");
	if(msgType==MSG_LAP_WSPOLRZEDNE_LOTNISKA)
	{

		COUT_SAM("odebralem lotnisko: "<<msg.getNazwaLotniska()<<", pozycja " <<msg.getPosition().toString() <<std::endl);
		if(msg.getIdHelper()==idLotniskaDst)
		{
			dstPos=msg.getPosition();
			dstLotnisko=msg.getNazwaLotniska();
			COUT_SAM("lotnisko docelowe sie nazywa:"<<dstLotnisko<<"\n");
		}
		else if((msg.getIdHelper()==idLotniskaSrc))	
		{
				srcPos=msg.getPosition();
				srcLotnisko= msg.getNazwaLotniska();
				COUT_SAM("lotnisko src sie nazywa:"<<srcLotnisko<<"\n");
		}
		WiezaInfo info;
		info.pos = msg.getPosition();
		info.id=msg.getIdWiezy();
		info.nazwa=msg.getNazwaLotniska();
		lotniska.push_back(info);
	}
	else if(msgType==MSG_LAP_LICZBE_LOTNISK)															
	{
		COUT_SAM("odebralem liczbe lotnisk: "<<msg.getLiczbaLotnisk()<<std::endl);
		liczbaLotnisk=msg.getLiczbaLotnisk();
	}
	else if(msgType==MSG_ZEZWOLENIE_NA_START)															
	{
		COUT_SAM("odebralem zezwolenia na start z czesem: "<<msg.getCzasOczekiwania()<<std::endl);
		czasOczekiwania=msg.getCzasOczekiwania();
		gotowyDoStartu=(czasOczekiwania==0);
	}
	else if(msgType==MSG_SIE_ZESRRRALES)															
	{
		COUT_SAM("dostlem awarie co sie nazywa"<<msg.getNazwaAwarii()<<"\n");
		awarie.push_back(msg);
		rozpatrzAwarie(msg.getTypAwarii(),msg.getMocAwarii(),msg.getNazwaAwarii());
	}
	else if(msgType==MSG_SIE_ODESRALES)	//wiadomosc,ze wystapila awaria														
	{
		COUT_SAM("odebralem info o usunieciu awarii co sie nazywa "<<msg.getNazwaAwarii()<<std::endl);
		msg.getIdHelper();//id awarii, ba jak dostaniemy info,ze awaria sie skonczyla, to musimy wiedziec ktora		
		usunAwarie(msg);
	}
	else if(msgType==MSG_ZMIEN_KURS)															
	{
		COUT_SAM("odebralem polecenie zmiany kursu: "<<msg.getNazwaLotniska()<<std::endl);
//		position newPos = msg.getPosition();
		position newPos;
		WiezaInfo nowaWiezaInfo = znajdzNajblizszeLotnisko(idAktWiezy);
		newPos = nowaWiezaInfo.pos;	
		dstPos.first=newPos.first;
		dstPos.second=newPos.second;
		dstLotnisko=nowaWiezaInfo.nazwa;
	}
	else if(msgType==MSG_MOZESZ_LADOWAC)															
	{
		COUT_SAM("odebralem info,ze moge ladowac od: "<<msg.getIdHelper()<<std::endl);
		
		czasOczekiwania=msg.getCzasOczekiwania();
		COUT_SAM("mam poczekac"<<czasOczekiwania);
		if(czasOczekiwania==0)
		{
			currPos=dstPos;
			naZiemiJest=true;
			COUT_SAM("laduje");
		}

			
	}
	else if(msgType==MSG_GON_SIE)															
	{
		COUT_SAM("zostalem hangorowany "<<std::endl);
		wyslijInfoOZakonczeniuSmigania(dstLotnisko);
		COUT_SAM("Koniec zabawy.");
		isRunning=false;
	}
	else if(msgType==MSG_RES_ZAPODAJ_DANE_TECHNICZNE)															
	{
		
		COUT_SAM("dostaje dane o samolocie "<<std::endl);
		lvlPaliwa = 	msg.getFuelLvl();
		v=msg.getPredkosc();
		zuzyciePaliwa=msg.getSpalanie();
		COUT_SAM("        poziom paliwa: "<<lvlPaliwa<<"\n        predkosc: "<<v<<"\n        spalanie: "<<zuzyciePaliwa<<std::endl);
		zuzyciePaliwa/=100;
		v/=3600;			
	}					
}

WiezaInfo Samolot::znajdzNajblizszeLotnisko(int idZlejWiezy = -1)
{
	float minOdl=1000000000;
	WiezaInfo debesciak;
	float odl;
	
	for (int i=0; i<lotniska.size(); ++i)
	{
		odl = obliczOdleglosc(currPos, lotniska[i].pos) ;
		if ((odl<minOdl) && (lotniska[i].id != idZlejWiezy))
			{	
			minOdl = odl;
			debesciak = lotniska[i];
			}
	}	
	return debesciak;
}

float Samolot::obliczOdleglosc(position a,position b)
{
	a.first = (float)(pi*a.first)/180;
	a.second = (float)(pi*a.second)/180;
	b.first = (float)(pi*b.first)/180;
	b.second = (float)(pi*b.second)/180;

	float distY = 6378 * (b.first - a.first);
	float distX = 6378 * (b.second - a.second);
	
	return sqrt(distY*distY + distX*distX);
}
void Samolot::powiadomOZmianieWiezy()
{
	SamolotMessage msg;
	msg.setMsgDst(WIEZA_MOD);
	msg.setIdHelper(idAktWiezy);
	msg.setIdWiezy(idAktWiezy);
	msg.setTypSamolotu(typSamolotu);
	COUT_SAM("zmieniam wieze na "<<idAktWiezy<<"!!\n");
	msg.setSendMsgType(MSG_NOWA_MASZYNA_W_STREFIE);
	msg.sendSamolotMessage();
}
void Samolot::powiadomOOpuszczeniuStrefy()
{
	SamolotMessage msg;
	msg.setMsgDst(WIEZA_MOD);
	msg.setIdHelper(idAktWiezy);
	msg.setIdWiezy(idAktWiezy);
	COUT_SAM("opuszczam wieze!\n");
	msg.setSendMsgType(MSG_OPUSCILES_STREFE_WPLYWOW);
	msg.sendSamolotMessage();
}
void Samolot::wyslijGotowoscDoStartu()
{
	SamolotMessage msg;
	msg.setMsgDst(WIEZA_MOD);
	msg.setIdHelper(idAktWiezy);
	msg.setTypSamolotu(typSamolotu);
	msg.setSendMsgType(MSG_ZGLOSZENIE_GOTOWOSCI_DO_STARTU);
	msg.sendSamolotMessage();
}
void Samolot::rozpatrzAwarie(int typAwarii, int moc,std::string nazwaAwarii)
{
	if(typAwarii==AWARIA_SILNIKA)
	{
		COUT_SAM("rozpatruje awarie silnika o mocy "<<moc<<".\n");
		v= ((SKALAAWARII-moc)*v)/SKALAAWARII;
		if(v<(150/(60*timeScale)))
		{
			wyslijInfoOZakonczeniuSmigania("null");
			powiadomOOpuszczeniuStrefy();
			COUT_SAM("BLAHAHAHAHA!!!!!!!!!\n samolot spadl\n");
			isRunning=false;
		}
	}
	else if(typAwarii==AWARIA_PALIWA)
	{
     		COUT_SAM("rozpatruje awarie paliwa o mocy "<<moc<<".\n");
			zuzyciePaliwa=((SKALAAWARII-moc)*zuzyciePaliwa)/SKALAAWARII;
	}
}
void Samolot::wyslijInfoOZakonczeniuSmigania(std::string dstAAA)
{
	SamolotMessage msg;
	msg.setMsgDst(ZDARZENIA_MOD);
	msg.setSendMsgType(MSG_KONCZE_SMIGAC);
	msg.setNazwaLotniska(dstAAA);
	COUT_SAM("lotnisko to:"<<dstAAA);
	msg.sendSamolotMessage();
}

void Samolot::poprosOPozwolenieNaLadowanie()
{
	SamolotMessage msg;
	msg.setMsgDst(WIEZA_MOD);
	msg.setSendMsgType(MSG_ZGLOSZENIE_GOTOWOSCI_DO_LADOWANIA);
	msg.setIdHelper(idAktWiezy);
	msg.sendSamolotMessage();
}
void Samolot::usunAwarie(SamolotMessage msg)
{	
	for(int i=0;i<awarie.size();++i)
	{
		if(awarie[i].getNazwaAwarii()==msg.getNazwaAwarii())
		{
			usunSkutki(msg.getTypAwarii(),msg.getMocAwarii());
			awarie.erase(awarie.begin()+i);
		}
	}
}
void Samolot:: powiadomLotniskoOPojawieniuSie()
{
		SamolotMessage msg;
		msg.setMsgDst(WIEZA_MOD);
		msg.setIdHelper(idAktWiezy);
		msg.setTypSamolotu(typSamolotu);
		msg.setSendMsgType(MSG_POJAWIAM_SIE_NA_LOTNISKU);
		msg.sendSamolotMessage();
}
void Samolot:: usunSkutki(int typAwarii, int moc)
{
	

	if(typAwarii==AWARIA_SILNIKA)
	{
		COUT_SAM("usuwama awarie silnika o mocy:" << moc << "\n" );
		COUT_SAM("towja stara v=" << v << "\n" );
		v=(v*SKALAAWARII)/(SKALAAWARII-moc);		
		COUT_SAM("nowa v=" << v << "\n" );
	}
	else if(typAwarii==AWARIA_PALIWA)
	{
			zuzyciePaliwa=(zuzyciePaliwa*SKALAAWARII)/(SKALAAWARII-moc);		
	}
}
